/**
 * 
 */
package hanto.studentdlally.tournament;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import hanto.common.HantoException;
import hanto.studentdlally.common.Coordinate;
import hanto.studentdlally.common.HantoBoard;
import hanto.studentdlally.common.HantoPiece;
import hanto.studentdlally.delta.DeltaHantoGame;
import hanto.tournament.HantoGamePlayer;
import hanto.tournament.HantoMoveRecord;
import hanto.util.HantoCoordinate;
import hanto.util.HantoPieceType;
import hanto.util.HantoPlayerColor;

/**
 * @author Doug
 * @version 1
 */
public class DeltaHantoPlayer implements HantoGamePlayer {

	private final HantoPlayerColor myColor;
	// Constructor specifies this, not sure why I care
	private final DeltaHantoGame game;
	private int sparrowsPlayed, crabsPlayed;
	private final Random r;


	/** Constructor for a DeltaHantoPlayer
	 * @param color the color of this player
	 * @param isFirst is this player first
	 */
	public DeltaHantoPlayer(HantoPlayerColor color, boolean isFirst){
		myColor = color;
		game = new DeltaHantoGame();
		sparrowsPlayed = crabsPlayed = 0;
		r = new Random(System.currentTimeMillis());
	}

	/* (non-Javadoc)
	 * @see hanto.tournament.HantoGamePlayer#makeMove(hanto.tournament.HantoMoveRecord)
	 */
	@Override
	public HantoMoveRecord makeMove(HantoMoveRecord opponentsMove) {
		// If the opponent moved, record it
		if(opponentsMove != null){
			try {
				game.makeMove(opponentsMove.getPiece(), opponentsMove.getFrom(), opponentsMove.getTo());
			} catch (HantoException e) {
				System.err.println("Oops, dlally's game thinks the opponent's move was invalid. Unspecified behavior from here on in");
			}
		}

		final HantoMoveRecord myNextMove = findValidMove(game, myColor, opponentsMove);
		if(myNextMove == null){
			return null;
		}
		try {
			game.makeMove(myNextMove.getPiece(), myNextMove.getFrom(), myNextMove.getTo());
		} catch (HantoException e) {
			e.printStackTrace();
		}

		return myNextMove;
	}

	/**
	 * Finds a valid move in the game for the given player color. Does not guarantee that the player should be the next one to move
	 * @param game
	 * @return
	 */
	private HantoMoveRecord findValidMove(DeltaHantoGame game, HantoPlayerColor myColor, HantoMoveRecord prevMove) {
		HantoMoveRecord nextMove = null;
		// If we haven't played our butterfly, play it
		if(!game.hasPlayedButterfly(myColor)){
			final HantoPieceType type = HantoPieceType.BUTTERFLY;
			HantoCoordinate to = new Coordinate(0,0);
			//See if the other player has already played
			if(prevMove != null){
				to = selectOpenAdjacentHex(new Coordinate(0,0));
			}
			nextMove = new HantoMoveRecord(type, null, to);
		}
		// Next, play any crabs that we have remaining
		else if(crabsPlayed < 4){
			//Loop until we get a move
			while(nextMove == null){
				HantoPiece piece = selectOneOfMyPieces(myColor);
				HantoCoordinate to = selectOpenAdjacentHex(piece.getCoordinate());
				if(game.isValidMove(HantoPieceType.CRAB, null, to)){
					nextMove = new HantoMoveRecord(HantoPieceType.CRAB, null, to);
					crabsPlayed++;
				}
				// Otherwise, that move is invalid, just let RNG pick another one
			}
		}
		// Next, play any sparrows that we have missing
		else if(sparrowsPlayed < 4){
			//Loop until we get a move
			while(nextMove == null){
				HantoPiece piece = selectOneOfMyPieces(myColor);
				HantoCoordinate to = selectOpenAdjacentHex(piece.getCoordinate());
				if(game.isValidMove(HantoPieceType.SPARROW, null, to)){
					nextMove = new HantoMoveRecord(HantoPieceType.SPARROW, null, to);
					sparrowsPlayed++;
				}
				// Otherwise, that move is invalid, just let RNG pick another one
			}
		}
		//If we have played all our pieces, start moving them around. If after 1000 itterations, we cant find a move, just surrender
		else{
			for(int i = 0; nextMove == null && i < 1000; i++){
				HantoPiece piece = selectOneOfMyPieces(myColor);
				HantoCoordinate to = null;
				// Butterflies and crabs walk one hex
				if(piece.getType() == HantoPieceType.BUTTERFLY || piece.getType() == HantoPieceType.CRAB){
					to = selectSlidableAdjacentHex(piece.getCoordinate());
				}
				else if(piece.getType() == HantoPieceType.SPARROW){
					to = selectOpenHex(game.getBoard());
				}
				if(game.isValidMove(piece.getType(), piece.getCoordinate(), to)){
					nextMove = new HantoMoveRecord(piece.getType(), piece.getCoordinate(), to);
				}
			}
		}
		return nextMove;
	}

	/**
	 * @param coordinate
	 * @return
	 */
	private HantoCoordinate selectSlidableAdjacentHex(HantoCoordinate coord) {
		final List<HantoCoordinate> possibleMoves = new ArrayList<HantoCoordinate>();
		// Brute force the six cases
		final HantoCoordinate up = new Coordinate(coord.getX(), coord.getY() + 1);
		final HantoCoordinate upr = new Coordinate(coord.getX() + 1, coord.getY());
		final HantoCoordinate upl = new Coordinate(coord.getX() - 1, coord.getY() + 1);
		final HantoCoordinate dwn = new Coordinate(coord.getX(), coord.getY() - 1);
		final HantoCoordinate dwnr = new Coordinate(coord.getX() + 1, coord.getY() - 1);
		final HantoCoordinate dwnl = new Coordinate(coord.getX() - 1, coord.getY());
		// Try to slide up
		if(game.getBoard().getPieceByCoordinates(up) == null &&
				game.getBoard().getPieceByCoordinates(upr) == null &&
				game.getBoard().getPieceByCoordinates(upl) == null){
			possibleMoves.add(up);
		}
		// Try up right
		if(game.getBoard().getPieceByCoordinates(upr) == null &&
				game.getBoard().getPieceByCoordinates(up) == null &&
				game.getBoard().getPieceByCoordinates(dwnr) == null){
			possibleMoves.add(upr);
		}
		//Try down right
		if(game.getBoard().getPieceByCoordinates(dwnr) == null &&
				game.getBoard().getPieceByCoordinates(upr) == null &&
				game.getBoard().getPieceByCoordinates(dwn) == null){
			possibleMoves.add(dwnr);
		}
		// Try down
		if(game.getBoard().getPieceByCoordinates(dwn) == null &&
				game.getBoard().getPieceByCoordinates(dwnr) == null &&
				game.getBoard().getPieceByCoordinates(dwnl) == null){
			possibleMoves.add(dwn);
		}
		// Try down left
		if(game.getBoard().getPieceByCoordinates(dwnl) == null &&
				game.getBoard().getPieceByCoordinates(dwn) == null &&
				game.getBoard().getPieceByCoordinates(upr) == null){
			possibleMoves.add(dwnl);
		}
		//Try up left
		if(game.getBoard().getPieceByCoordinates(upr) == null && 
				game.getBoard().getPieceByCoordinates(dwnl) == null &&
				game.getBoard().getPieceByCoordinates(up) == null){
			possibleMoves.add(upr);
		}
		if(possibleMoves.size() == 0){
			return null;
		}
		return possibleMoves.get(r.nextInt(possibleMoves.size()));
	}

	/** Selects a random piece played by the given color
	 * @param myColor select a piece of this color
	 * @return a HantoPiece of the specified color
	 */
	private HantoPiece selectOneOfMyPieces(HantoPlayerColor myColor){
		final List<HantoPiece> myPieces = game.getBoard().getAllPieces(myColor);
		return myPieces.get(r.nextInt(myPieces.size()));
	}

	/**
	 * @param hantoCoordinate
	 * @return
	 */
	private HantoCoordinate selectOpenAdjacentHex(HantoCoordinate coord){
		final List<HantoCoordinate> openCoords = new ArrayList<HantoCoordinate>();
		// Check the 6 surrounding hexes
		if(game.getBoard().getPieceByCoordinates(new Coordinate(coord.getX(), coord.getY() + 1)) == null){
			openCoords.add(new Coordinate(coord.getX(), coord.getY() + 1));
		}

		//Check above and to the right
		if(game.getBoard().getPieceByCoordinates(new Coordinate(coord.getX() + 1, coord.getY())) == null){
			openCoords.add(new Coordinate(coord.getX() + 1, coord.getY()));
		}

		//Check below and to the right
		if(game.getBoard().getPieceByCoordinates(new Coordinate(coord.getX() + 1, coord.getY() - 1)) == null){
			openCoords.add(new Coordinate(coord.getX() + 1, coord.getY() - 1));
		}

		//Check below
		if(game.getBoard().getPieceByCoordinates(new Coordinate(coord.getX(), coord.getY() - 1)) == null){
			openCoords.add(new Coordinate(coord.getX(), coord.getY() - 1));
		}

		// Check below and to the left
		if(game.getBoard().getPieceByCoordinates(new Coordinate(coord.getX() - 1, coord.getY())) == null){
			openCoords.add(new Coordinate(coord.getX() - 1, coord.getY()));
		}

		// Check above and to the left
		if(game.getBoard().getPieceByCoordinates(new Coordinate(coord.getX() - 1, coord.getY() + 1)) == null){
			openCoords.add(new Coordinate(coord.getX() - 1, coord.getY() + 1));
		}
		if(openCoords.size() == 0){
			return null;
		}
		return openCoords.get(r.nextInt(openCoords.size()));
	}

	private HantoCoordinate selectOpenHex(HantoBoard b){
		HantoCoordinate coord = null;
		final List<HantoPiece> pieces = b.getAllPieces();
		while(coord == null){
			coord = selectOpenAdjacentHex(pieces.get(r.nextInt(pieces.size())).getCoordinate());
		}

		return coord;
	}

}
